using System;
using System.Linq;
using UnityEngine;

namespace Prota.Unity
{
    public static partial class UnityMethodExtensions
    {
        #region Universal Components
        public static T GetOrCreate<T>(this Component x) where T: Component
        {
            if(x.TryGetComponent<T>(out var res)) return res;
            return x.gameObject.AddComponent<T>();
        }
        
        // 新建时返回 false, 获取已有组件返回 true.
        public static bool GetOrCreate<T>(this Component x, out T res) where T: Component
        {
            if(x.TryGetComponent<T>(out res)) return true;
            res = x.gameObject.AddComponent<T>();
            return false;
        }
        
        public static T Clone<T>(this T g, Transform parent = null) where T: Component
        {
            return g.gameObject.Clone(parent).GetComponent<T>();
        }
        
        public static T CloneAsTemplate<T>(this T g, Transform parent) where T: Component
        {
            return g.gameObject.CloneAsTemplate(parent).GetComponent<T>();
        }
        
        public static T CloneAsTemplate<T>(this T g) where T: Component
        {
            return g.gameObject.CloneAsTemplate().GetComponent<T>();
        }
        
        
        
        // 设置 Component 对应的 Game Object 是否激活.
        public static T SetActive<T>(this T a, bool activate = true) where T: Component
        {
            if(a.gameObject.activeSelf == activate) return a;
            a.gameObject.SetActive(activate);
            return a;
        }
        
        public static T GetComponentAsserted<T>(this Component a) where T: Component
        {
            if(a.TryGetComponent<T>(out var res)) return res;
            throw new Exception($"{a.GetNamePath()}: No {typeof(T).Name} found.");
        }
        
        public static T GetComponentCritical<T>(this Component a) where T: Component
        {
            if(a.TryGetComponent<T>(out var res)) return res;
            Debug.LogError($"{a.GetNamePath()}: No {typeof(T).Name} found.");
            return null;
        }
        #endregion
        
        // ============================================================================================================
        // ============================================================================================================
        #region RectTransform
        
        public static RectTransform RectTransform(this Component x) => x.transform as RectTransform;
        
        public static Rect WorldRect(this RectTransform tr)
        {
            var corners = new Vector3[4];
            tr.GetWorldCorners(corners);
            var min = corners[0];
            var max = corners[2];
            return Rect.MinMaxRect(min.x, min.y, max.x, max.y);
        }
        
        #endregion
        
        // ====================================================================================================
        // ====================================================================================================
        
        #region Camera
        public static Rect GetCameraWorldView(this Camera cam)
        {
            var bottomLeft = cam.ViewportPointToRay(Vector2.zero).HitXYPlane();
            var topRight = cam.ViewportPointToRay(Vector2.one).HitXYPlane();
            return new Rect(bottomLeft, topRight - bottomLeft);
        }
        #endregion
        
        
        // ====================================================================================================
        // ====================================================================================================
        
        #region Animator & AnimationController
        
        public static float GetDurationOfCurrentPlaying(this Animator animator)
        {
            var controller = animator.runtimeAnimatorController;
            var state = animator.GetCurrentAnimatorStateInfo(0);
            var clipName = state.shortNameHash;
            return controller.GetDuration(clipName);
        }
        
        public static float GetDuration(this RuntimeAnimatorController controller, int hash)
        {
            var clips = controller.animationClips;
            foreach (var clip in clips)
            {
                if (Animator.StringToHash(clip.name) == hash)
                {
                    return clip.length;
                }
            }
            Debug.LogError($"AnimationClip with hash [{hash}] in [{controller.name}] not found ");
            Debug.LogError(clips.Select(x => x.name + " " + Animator.StringToHash(x.name)).ToStringJoined("\n"));
            return 1.0f;
        }
        
        public static float GetDuration(this RuntimeAnimatorController controller, string clipName)
        {
            var clips = controller.animationClips;
            foreach (var clip in clips)
            {
                if (clip.name == clipName)
                {
                    return clip.length;
                }
            }
            Debug.LogError($"AnimationClip [{clipName}] in [{controller.name}] not found ");
            Debug.LogError(clips.Select(x => x.name).ToStringJoined("\n"));
            return 1.0f;
        }
        
        #endregion
        
    }
    
}
